package com.jhf.youke.order.app.executor;

import com.jhf.youke.core.ddd.BaseAppService;
import com.jhf.youke.core.ddd.BaseEvent;
import com.jhf.youke.core.entity.Pagination;
import com.jhf.youke.core.entity.Response;
import com.jhf.youke.core.entity.User;
import com.jhf.youke.core.utils.CacheUtils;
import com.jhf.youke.core.utils.Constant;
import com.jhf.youke.core.utils.StringUtils;
import com.jhf.youke.order.app.feign.ProductFeign;
import com.jhf.youke.order.app.feign.WechatFeign;
import com.jhf.youke.order.app.feign.hystrix.ProductHystrix;
import com.jhf.youke.order.domain.converter.OrderConverter;
import com.jhf.youke.order.domain.event.OrderEvent;
import com.jhf.youke.order.domain.exception.OrderException;
import com.jhf.youke.order.domain.model.Do.OrderDo;
import com.jhf.youke.order.domain.model.dto.OrderDto;
import com.jhf.youke.order.domain.model.dto.VideoOrderDto;
import com.jhf.youke.order.domain.model.po.OrderPo;
import com.jhf.youke.order.domain.model.vo.OrderVo;
import com.jhf.youke.order.domain.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.map.AbstractReferenceMap;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;


/**
 * @author RHJ
 */
@Slf4j
@Service
public class OrderAppService extends BaseAppService {

    @Resource
    private OrderService orderService;

    @Resource
    private OrderConverter orderConverter;

    @Resource
    /** 容器事件由容器触发 **/
    private ApplicationContext applicationContext;

    @Resource
    private ProductFeign productFeign;

    @Resource
    private WechatFeign wechatFeign;


    public boolean update(OrderDto dto) {
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return orderService.update(orderDo);
    }

    public boolean delete(OrderDto dto) {
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return orderService.delete(orderDo);
    }


    public boolean insert(OrderDto dto) {
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return orderService.insert(orderDo);
    }

    public Optional<OrderVo> findById(Long id) {
        return orderService.findById(id);
    }


    public boolean remove(Long id) {
        return orderService.remove(id);
    }


    public List<OrderVo> findAllMatching(OrderDto dto) {
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return orderService.findAllMatching(orderDo);
    }


    public Pagination<OrderVo> selectPage(OrderDto dto) {
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return orderService.selectPage(orderDo);
    }

    public Boolean completed(OrderDto dto){
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return  orderService.completed(orderDo);
    }

    public Boolean confirm(OrderDto dto){
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return  orderService.confirm(orderDo);
    }

    public Boolean deliverGoods(OrderDto dto){
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return  orderService.deliverGoods(orderDo);
    }

    public Boolean termination(OrderDto dto){
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return  orderService.termination(orderDo);
    }

    public Boolean paid(OrderDto dto){
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        return  orderService.paid(orderDo);
    }

    public Pagination<OrderVo> listBySale(String token, OrderDto orderDto){
        User user = CacheUtils.getUser(token);
        OrderDo orderDo = orderConverter.dto2Do(orderDto);
        orderDo.setSalesman(user.getId());
        return  orderService.selectPage(orderDo);
    }

    /** 订单提交计算佣金 **/
    public Boolean submit(OrderDto dto) {
        Boolean res = false;
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        orderService.confirm(orderDo);
        // 提交后发送MQ计算佣金
        OrderEvent event = new OrderEvent(this, BaseEvent.EVENT_APPLICATION,"createCommissionConsume",
                orderDo.sendCommissionMsg());
        applicationContext.publishEvent(event);
        res = true;
        return res;
    }

    public Optional<OrderVo> detailById(Long id){
        return orderService.detailById(id);
    }

    /** 订单保存, 并将客户信息及收货地址发送CRM **/
    public Boolean save(OrderDto dto) {

        dto.check();
        OrderDo orderDo =  orderConverter.dto2Do(dto);
        if(dto.isNew()){
            orderService.insert(orderDo);
        }else {
            orderService.update(orderDo);
        }
        // 只有新增时才进行判断
        if(Constant.IF_NEW.equals(dto.getCustomer().getIfNew())
                || Constant.IF_NEW.equals(dto.getCustomer().getAddress().getIfNew()) ){
            OrderEvent event = new OrderEvent(this, BaseEvent.EVENT_APPLICATION,"createCustomerConsume",
                    orderDo.sendCustomerMsg());
            applicationContext.publishEvent(event);
        }

        return true;
    }

    public Boolean payNotify(Long id) {
        OrderVo orderVo = orderService.findById(id).orElseThrow(() -> new OrderException(Constant.FIND_NOT_DATA));
        OrderDo orderDo = new OrderDo();
        orderDo.setId(orderVo.getId());
        orderDo.paid();
        return orderService.update(orderDo);
    }

    public Boolean videoOrderPayNotify(String code) {
        return orderService.videoOrderPayNotify(code);
    }

    @Transactional(rollbackFor = Exception.class)
    public Map<String,String> videoSave(String token, VideoOrderDto dto) {
        User user = CacheUtils.getUser(token);
        Map<String,Object> productRelease = getProductRelease(dto.getProductReleaseId());

        OrderPo orderPo = orderService.videoSave(productRelease, user);
        String productName = StringUtils.chgNull(productRelease.get("name"));

        return videoWxPay(orderPo.getCode(), orderPo.getSumFee(), user.getOpenId(), productName);
    }

    @Transactional(rollbackFor = Exception.class)
    public Map<String,String> videoWxPay(String outTradeNo, BigDecimal fee, String openId, String description) {
        Map<String,Object> payParam = new HashMap<>(6);
        payParam.put("outTradeNo", outTradeNo);
        payParam.put("fee", fee);
        payParam.put("openId", openId);
        payParam.put("description", description);
        payParam.put("attach", "videoOrder");
        Response<Map<String, String>> data = wechatFeign.jsApiPay(payParam);
        if(data != null && data.getCode() == 0){
            return data.getData();
        }else {
            throw new OrderException("创建订单失败");
        }
    }

    /**
     * 视频订单重新支付
     * @param orderId
     * @param user
     * @return
     */
    public Map<String,String> videoAgainPay(Long orderId, User user) {
        OrderVo vo = orderService.videoAgainPay(orderId, user);
        return videoWxPay(vo.getCode(), vo.getSumFee(), user.getOpenId(), vo.getProductName());
    }

    public Map<String, Object> getProductRelease(Long productReleaseId) {
        Map<String,Object> productRelease = productFeign.getVideo("", Map.of("id", productReleaseId));
        if(productRelease == null){
            throw new OrderException("商品不存在");
        }
        String outStatus = "2";
        String statusCode = "status";
        if(outStatus.equals(StringUtils.chgNull(productRelease.get(statusCode)))){
            throw new OrderException("商品已下架");
        }
        return productRelease;
    }

    public Map<String, Object> videoProductReleaseInfo(String token, VideoOrderDto dto) {
        User user = CacheUtils.getUser(token);
        Map<String,Object> productRelease = productFeign.getVideo(token, Map.of("id", dto.getProductReleaseId()));
        Long id = StringUtils.toLong(productRelease.get("id"));
        productRelease.put("isplay", orderService.isBuyProductRelease(id, user));
        return productRelease;
    }

    public Pagination<OrderVo> getVideoPageList(OrderDto dto) {
        return orderService.getVideoPageList(dto);
    }
}
